package com.example.xiaoshuo_yudu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.xiaoshuo_yudu.entity.Message.ChapterPreCacheMessage;
import com.example.xiaoshuo_yudu.entity.entity.Books;
import com.example.xiaoshuo_yudu.mapper.BooksMapper;
import com.example.xiaoshuo_yudu.mapper.ChapterTablesMapper;
import com.example.xiaoshuo_yudu.service.BooksService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.xiaoshuo_yudu.utils.Const;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * <p>
 * 小说基本信息表 服务实现类
 * </p>
 *
 * @author xusen
 * @since 2025-07-24
 */
@Service
@Slf4j
public class BooksServiceImpl extends ServiceImpl<BooksMapper, Books> implements BooksService {
    @Autowired
    private BooksMapper booksMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ChapterTablesMapper chapterTablesMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    // 锁对象（防止缓存击穿的并发问题）
//    private final ReentrantLock lock = new ReentrantLock();
    // 新增：细粒度锁的映射容器（key为业务标识，如name、bookId等，value为对应的锁）
    private final ConcurrentHashMap<String, Lock> lockMap = new ConcurrentHashMap<>();

    // 新增：获取当前key对应的锁（不存在则创建）
    private Lock getLock(String key) {
        // 确保线程安全：computeIfAbsent是原子操作，避免重复创建锁
        return lockMap.computeIfAbsent(key, k -> new ReentrantLock());
    }
    /**
     * 返回前端text数组数据，包含各类榜单
     *
     * @return
     */
    @Override
    public List<Object> rank() {
        Object key = Const.RANK_KEY;
//        1.查询缓存
        List<Object> rankData =(List<Object>) redisTemplate.opsForValue().get(key);
        if (rankData != null) {
            // 2 缓存命中
            return rankData;
        }
        Lock lock = getLock("rank");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            rankData = (List<Object>) redisTemplate.opsForValue().get(key);
            if (rankData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return rankData;
            }
            QueryWrapper<Books> queryWrapper = new QueryWrapper<>();
            //最后结果
            ArrayList<Object> list = new ArrayList<>();
            //获取全站小说名字和简介
            List<Map<String, Object>> WholeSite = getCoverUrlAndIntro(queryWrapper);
            HashMap<Object, Object> WholeSiteMap = new HashMap<>();
            //        各类列表
            List<Map<String, Object>> Wholelist = getNameList(1);
            WholeSiteMap.put("title", "全站");
            WholeSiteMap.put("imgs", WholeSite);
            WholeSiteMap.put("sub", Wholelist);
            //获取男生小说名字和简介
            queryWrapper.eq("target_user", 1);
            List<Map<String, Object>> BoySite = getCoverUrlAndIntro(queryWrapper);
            HashMap<Object, Object> BoySiteMap = new HashMap<>();
            List<Map<String, Object>> Boylist = getNameList(2);
            BoySiteMap.put("title", "男生");
            BoySiteMap.put("imgs", BoySite);
            BoySiteMap.put("sub", Boylist);
            //获取女生小说名字和简介
            queryWrapper.clear();
            queryWrapper.eq("target_user", 2);
            List<Map<String, Object>> GirlSite = getCoverUrlAndIntro(queryWrapper);
            HashMap<Object, Object> GirlSiteMap = new HashMap<>();
            List<Map<String, Object>> Girllist = getNameList(3);
            GirlSiteMap.put("title", "女生");
            GirlSiteMap.put("imgs", GirlSite);
            GirlSiteMap.put("sub", Girllist);
            //获取新书小说名字和简介
            queryWrapper.clear();
            queryWrapper.gt("create_time", LocalDateTime.now().minusDays(30));
            List<Map<String, Object>> NewSite = getCoverUrlAndIntro(queryWrapper);
            HashMap<Object, Object> NewSiteMap = new HashMap<>();
            List<Map<String, Object>> Newlist = getNameList(4);
            NewSiteMap.put("title", "新书");
            NewSiteMap.put("imgs", NewSite);
            NewSiteMap.put("sub", Newlist);
            //获取完本小说名字和简介
            queryWrapper.clear();
            queryWrapper.eq("status", "完结");
            List<Map<String, Object>> FinishedSite = getCoverUrlAndIntro(queryWrapper);
            HashMap<Object, Object> FinishedSiteMap = new HashMap<>();
            List<Map<String, Object>> Finishedlist = getNameList(5);
            FinishedSiteMap.put("title", "完本");
            FinishedSiteMap.put("imgs", FinishedSite);
            FinishedSiteMap.put("sub", Finishedlist);
            //构建最后的
            list.add(WholeSiteMap);
            list.add(BoySiteMap);
            list.add(GirlSiteMap);
            list.add(NewSiteMap);
            list.add(FinishedSiteMap);
            if (list == null || list.isEmpty()) {
                // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("榜单数据为空");
            }
            redisTemplate.opsForValue().set(key, list, 60 * 60 * 2, TimeUnit.SECONDS);
            return list;
        }finally {
            lock.unlock();
        }
    }
    /**
     * 获取小说名字和简介
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> getNameAndIntro() {
        Object key = Const.NAME_INTRO_KEY;
        //        1.查询缓存
        List<Map<String, Object>> getNameAndIntroData = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
        if (getNameAndIntroData != null) {
            // 2 缓存命中
            return getNameAndIntroData;
        }
        Lock lock = getLock("getNameAndIntroData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getNameAndIntroData = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
            if (getNameAndIntroData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getNameAndIntroData;
            }
            QueryWrapper<Books> queryWrapper = new QueryWrapper<>();
            queryWrapper
                    .orderByDesc("click_count")
                    .select("novel_name", "intro", "book_id")
                    .last("limit 5");
            List<Map<String, Object>> nameAndIntro = booksMapper.selectMaps(queryWrapper);
            for (int i = 0; i < nameAndIntro.size(); i++) {
                Object novel_name = nameAndIntro.get(i).get("novel_name");
                nameAndIntro.get(i).remove("novel_name");
                nameAndIntro.get(i).put("novelName", novel_name);
                Object book_id = nameAndIntro.get(i).get("book_id");
                nameAndIntro.get(i).remove("book_id");
                nameAndIntro.get(i).put("bookId", book_id);
            }
            if (nameAndIntro == null || nameAndIntro.isEmpty()) {
                // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("小说名称和简介数据为空");
            }
            redisTemplate.opsForValue().set(key, nameAndIntro, 60 * 70, TimeUnit.SECONDS);
            return nameAndIntro;
        }finally {
            lock.unlock();
        }
    }
    /**
     * 获取推荐书籍列表
     *
     * @return
     */
    @Override
    public List<Object> getRecommendAll() {
        String key = Const.RECOMMENDED_BOOKS;
        //        1.查询缓存
        List<Object> getRecommendAllData = (List<Object>) redisTemplate.opsForValue().get(key);
        if (getRecommendAllData != null) {
            // 2 缓存命中
            return getRecommendAllData;
        }
        Lock lock = getLock("getRecommendAllData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getRecommendAllData = (List<Object>) redisTemplate.opsForValue().get(key);
            if (getRecommendAllData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getRecommendAllData;
            }
            // 构建查询条件
            // 查询免费小说
            QueryWrapper<Books> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("is_vip", 0)
                    .orderByDesc("recommend_count", "category")
                    .select("novel_name", "category", "book_id")
                    .last("limit 22");
            List<Map<String, Object>> free = booksMapper.selectMaps(queryWrapper);
            // vip书籍查询
            QueryWrapper<Books> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("is_vip", 1)
                    .orderByDesc("recommend_count", "category")
                    .select("novel_name", "category", "book_id")
                    .last("limit 22");
            List<Map<String, Object>> vip = booksMapper.selectMaps(queryWrapper1);
//构建免费
            HashMap<Object, Object> hashMap = new HashMap<>();
            hashMap.put("title", "免费好书");
            ArrayList<Object> list1 = new ArrayList<>();
            ArrayList<Object> list2 = new ArrayList<>();
            ArrayList<Object> list3 = new ArrayList<>();
            for (Map<String, Object> map : free) {
                list1.add(map.get("novel_name"));
                list2.add(map.get("book_id"));
                list3.add(map.get("category"));
            }
            hashMap.put("novelName", list1);
            hashMap.put("bookId", list2);
            hashMap.put("category", list3);
//      构建vip
            ArrayList<Object> list4 = new ArrayList<>();
            ArrayList<Object> list5 = new ArrayList<>();
            ArrayList<Object> list6 = new ArrayList<>();
            HashMap<Object, Object> hashMap1 = new HashMap<>();
            hashMap1.put("title", "vip好书");
            for (Map<String, Object> map : vip) {
                list4.add(map.get("novel_name"));
                list5.add(map.get("book_id"));
                list6.add(map.get("category"));
            }
            hashMap1.put("novelName", list4);
            hashMap1.put("bookId", list5);
            hashMap1.put("category", list6);
            List<Object> list = new ArrayList<>();
            list.add(hashMap);
            list.add(hashMap1);
            if (list == null || list.isEmpty()) {
                // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("推荐书籍列表为空");
            }
            redisTemplate.opsForValue().set(key, list, 60 * 50, TimeUnit.SECONDS);
            return list;
        }finally {
            lock.unlock();
        }
    }
    /**
     * 获取男生频道书籍列表
     *
     * @return
     */
    @Override
    public List<Object> getBoyChannelBooks() {
        String key = Const.BOY_CHANNEL_BOOKS;
        List<Object> getBoyChannelBooksData = (List<Object>) redisTemplate.opsForValue().get(key);
        if (getBoyChannelBooksData != null) {
            // 2 缓存命中
            return getBoyChannelBooksData;
        }
        Lock lock = getLock("getBoyChannelBooksData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getBoyChannelBooksData = (List<Object>) redisTemplate.opsForValue().get(key);
            if (getBoyChannelBooksData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getBoyChannelBooksData;
            }
        // 构建查询条件
        QueryWrapper<Books> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("target_user", 1)
                .orderByDesc("click_count")
                .select("novel_name", "category", "book_id", "cover_url", "intro","click_count");
        List<Map<String, Object>> boyAll = booksMapper.selectMaps(queryWrapper);
        // 创建结果列表
        List<Object> resultList = new ArrayList<>();
        //抽取方法
        Map<String, Object> allStation =method1(boyAll);
        // 将"全站"分类项添加到结果列表
        resultList.add(allStation);
        //获取全部分类
        List<String> maps = booksMapper.getBoyList();

        // 构建获取频道书籍列表
        for (int i = 0; i < maps.size(); i++) {
            List<Map<String, Object>> Sub =SubcategoryChapters(maps.get(i),1);
            String a =maps.get(i).substring(0, 2);
            Map<String, Object> stringObjectMap = method2(Sub, a);
            resultList.add(stringObjectMap);
        }
        if (resultList == null || resultList.isEmpty()) {
            // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
            redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
            throw new RuntimeException("男生频道书籍列表为空");
        }
        redisTemplate.opsForValue().set(key, resultList, 60 * 61 , TimeUnit.SECONDS);

        return resultList;
    }finally {
            lock.unlock();
        }
    }

    /**
     * 获取女生频道书籍列表
     *
     * @return
     */
    @Override
    public List<Object> getGirlChannelBooks() {
        String key = Const.GIRL_CHANNEL_BOOKS;
        List<Object> getGirlChannelBooksData = (List<Object>) redisTemplate.opsForValue().get(key);
        if (getGirlChannelBooksData != null) {
            // 2 缓存命中
            return getGirlChannelBooksData;
        }
        Lock lock = getLock("getGirlChannelBooksData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getGirlChannelBooksData = (List<Object>) redisTemplate.opsForValue().get(key);
            if (getGirlChannelBooksData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getGirlChannelBooksData;
            }
        // 构建查询条件
        QueryWrapper<Books> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("target_user", 2)
                .orderByDesc("click_count")
                .select("novel_name", "category", "book_id", "cover_url", "intro","click_count");
        List<Map<String, Object>> girlAll = booksMapper.selectMaps(queryWrapper);
        // 创建结果列表
        List<Object> resultList = new ArrayList<>();
        //抽取方法
        Map<String, Object> allStation =method1(girlAll);
        // 将"全站"分类项添加到结果列表
        resultList.add(allStation);
        //获取全部分类
        List<String> maps = booksMapper.getGirlList();
        // 构建获取频道书籍列表
        for (int i = 0; i < maps.size(); i++) {
            List<Map<String, Object>> Sub =SubcategoryChapters(maps.get(i),2);
            String a =maps.get(i).substring(0, 2);
            Map<String, Object> stringObjectMap = method2(Sub, a);
            resultList.add(stringObjectMap);
        }
        if (resultList == null || resultList.isEmpty()) {
            // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
            redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
            throw new RuntimeException("女生频道书籍列表为空");
        }
        redisTemplate.opsForValue().set(key, resultList, 60 * 62 , TimeUnit.SECONDS);
        return resultList;
    }finally {
            lock.unlock();
        }
    }

    /**
     * 获取新人频道书籍列表
     *
     * @return
     */
    @Override
    public List<Object> getnewChannelBooks() {
        Object key = Const.NEW_CHANNEL_BOOKS;
        List<Object> getnewChannelBooksData = (List<Object>) redisTemplate.opsForValue().get(key);
        if (getnewChannelBooksData != null) {
            // 2 缓存命中
            return getnewChannelBooksData;
        }
        Lock lock = getLock("getnewChannelBooksData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getnewChannelBooksData = (List<Object>) redisTemplate.opsForValue().get(key);
            if (getnewChannelBooksData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getnewChannelBooksData;
            }
        QueryWrapper<Books> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .gt("create_time", LocalDateTime.now().minusDays(7))
                .orderByDesc("click_count")
                .select("novel_name", "category", "book_id", "cover_url", "intro","click_count");
        List<Map<String, Object>> newAll = booksMapper.selectMaps(queryWrapper);
        // 创建结果列表
        List<Object> resultList = new ArrayList<>();
        //抽取方法
        Map<String, Object> allStation =method1(newAll);
        // 将"全站"分类项添加到结果列表
        resultList.add(allStation);
        //获取全部分类
        List<String> maps = booksMapper.getNewList();
        for (int i = 0; i < maps.size(); i++) {
            //构建获取频道书籍列表
            QueryWrapper<Books> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1
//                    添加新书
                    .gt("create_time", LocalDateTime.now().minusDays(7))
                    .eq("category", maps.get(i))
                    .orderByDesc("click_count")
                    .select("novel_name", "category", "book_id", "cover_url", "intro","click_count");
            List<Map<String, Object>> Sub = booksMapper.selectMaps(queryWrapper1);
            //
            String a =maps.get(i).substring(0, 2);
            Map<String, Object> stringObjectMap = method2(Sub, a);
            resultList.add(stringObjectMap);
        }
            if (resultList == null || resultList.isEmpty()) {
                // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("新书频道书籍列表为空");
            }
        redisTemplate.opsForValue().set(key, resultList, 60 * 62 , TimeUnit.SECONDS);
        return resultList;
    }finally {
            lock.unlock();
        }
    }

    /**
     * 单本书的详细信息
     *
     * @param bookId
     * @return
     */
    @Override
    public Map<String, String> getListDetail(Integer bookId) {
        String key = Const.SUB_DTAIL_DATA + ":" + bookId;
        Map<String, String> getListDetailData = (Map<String, String>) redisTemplate.opsForValue().get(key);
        if (getListDetailData != null) {
            // 2 缓存命中
            return getListDetailData;
        }
        Lock lock = getLock("getListDetailData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getListDetailData = (Map<String, String>) redisTemplate.opsForValue().get(key);
            if (getListDetailData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getListDetailData;
            }
        Books books = this.getById(bookId);
        String novelName = books.getNovelName();
        String intro = books.getIntro();
        String coverUrl = books.getCoverUrl();
        String latestChapter = books.getLatestChapter();
        String updateTime = books.getUpdateTime();
        String author = books.getAuthor();
        String category = books.getCategory();

        Map<String, String> list = new HashMap<>();
        list.put("title", novelName);
        list.put("cover", coverUrl);
        list.put("author", author);
        list.put("category", category);
        list.put("lastUpdate", updateTime);
        list.put("latestChapter", latestChapter);
        list.put("intro", intro);
        if (list == null || list.isEmpty()) {
            // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
            redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
            throw new RuntimeException("单书列表为空");
        }
        redisTemplate.opsForValue().set(key, list, 1 , TimeUnit.HOURS );
        return list;
    }finally {
            lock.unlock();
        }
    }

    /**
     * 获取小说列表
     *
     * @param bookId
     * @return
     */
    @Override
    public List<Map<String, Object>> getChapterListByBookId(Integer bookId) {
        String key = Const.SUB_CHAPTER_LIST + ":" + bookId;
        List<Map<String, Object>> getChapterListByBookIdData = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
        if (getChapterListByBookIdData != null) {
            // 2 缓存命中
            return getChapterListByBookIdData;
        }
        Lock lock = getLock("getChapterListByBookIdData");
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            getChapterListByBookIdData = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
            if (getChapterListByBookIdData != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return getChapterListByBookIdData;
            }
            //获取章节表名
            String tableName = getTableName(bookId);
            //处理根据书id查询查出来的小说排序id和标题
            List<Map<String, Object>> maps = chapterTablesMapper.getChapterListByBookId(tableName, bookId).stream().map(map -> {
                Object chapter_title = map.get("chapter_title");
                Object chapter_order = map.get("chapter_order");
                map.put("title", chapter_title);
                map.put("id", chapter_order);
                map.remove("chapter_title");
                map.remove("chapter_order");
                return map;
            }).collect(Collectors.toList());
            if (maps == null || maps.isEmpty()) {
                // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("小说列表为空");
            }
            //30-60的随机数生成
            int random = (int) (Math.random() * 30 + 30);

            redisTemplate.opsForValue().set(key, maps, 60 * 10 * random, TimeUnit.SECONDS);
            return maps;
        }finally {
            lock.unlock();
        }
    }
    /**
     * 获取小说章节
     *
     * @param bookId
     * @param chapterId
     * @return
     */
    @Override
    public Map<String, Object> getChapterDetail(Integer bookId, Integer chapterId) {
        // 1. 第一步：优先触发下一章预缓存（在处理当前章节前执行）
        String tableName = this.getTableName(bookId);
        sendPreCacheMessage(bookId, chapterId + 1, tableName); // 预缓存下一章
        // 2. 尝试从缓存中获取章节数据
        String key = Const.SUB_CHAPTER_DATA + ":" + bookId + ":" + chapterId;
        Map<String, Object> cachedChapter = (Map<String, Object>) redisTemplate.opsForValue().get(key);
        if (cachedChapter != null) {
            // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//            renewCacheExpire(key, cachedChapter);
            return cachedChapter;
        }
        Lock lock = getLock(bookId.toString());
        try {
            lock.lock();// 同一时间只有一个线程查数据库，其他线程等待缓存更新
            cachedChapter = (Map<String, Object>) redisTemplate.opsForValue().get(key);
            if (cachedChapter != null) {
                // 2.1 缓存命中：如果是热门章节，延长过期时间（访问一次就续期）
//                renewCacheExpire(key, cachedChapter);
                return cachedChapter;
            }
            //获取章节详情
            Map<String, Object> chapterDetail = chapterTablesMapper.getChapterDetail(tableName, bookId, chapterId);

            // 3.3 处理章节数据（字段映射）
            if (chapterDetail == null || chapterDetail.isEmpty()) {
                // 3.3.1 缓存空结果（防止缓存穿透），过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("章节不存在"); // 或返回空对象，前端处理"章节不存在"
            }
            //获取小说真名
            Books books = this.getById(bookId);
            chapterDetail.put("novelName", books.getNovelName());
            chapterDetail.put("id", chapterId);
            chapterDetail.put("bookId", bookId);
            chapterDetail.put("title", chapterDetail.get("chapter_title"));
            chapterDetail.remove("chapter_title");
            chapterDetail.put("chapterContent", chapterDetail.get("chapter_content"));
            chapterDetail.remove("chapter_content");
            redisTemplate.opsForValue().set(key, chapterDetail, 10, TimeUnit.MINUTES);
            return chapterDetail;
        }finally {
            lock.unlock(); // 释放锁
        }
    }

    /***
     *获取各小说页主页头
     * @param name
     */
    @Override
    public List<Map<String, Object>> getBooksList(String name) {
        String key = Const.SUB_BOOKS_LIST + ":" + name;
        if(redisTemplate.hasKey(key)){
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                // 缓存存在但值为null（已处理过穿透），直接返回空列表
                return Collections.emptyList();
            }
            return (List<Map<String, Object>>) value;
        }
        Lock lock = getLock(name); // 用name作为锁的key
        try {
            lock.lock();
            if(redisTemplate.hasKey(key)){
                Object value = redisTemplate.opsForValue().get(key);
                if (value == null) {
                    // 缓存存在但值为null（已处理过穿透），直接返回空列表
                    return Collections.emptyList();
                }
                return (List<Map<String, Object>>) value;
            }
            QueryWrapper<Books> objectQueryWrapper = new QueryWrapper<>();
            objectQueryWrapper.eq("category", name)
                    .select("novel_name", "intro", "author","cover_url","book_id")
                    .orderByDesc("click_count")
                    .last("LIMIT 6");
            List<Map<String, Object>> collect = booksMapper.selectMaps(objectQueryWrapper).stream().map(map -> {
                map.put("title", map.get("novel_name"));
                map.remove("novel_name");
                map.put("cover", map.get("cover_url"));
                map.remove("cover_url");
                map.put("bookId", map.get("book_id"));
                map.remove("book_id");
                return map;
            }).collect(Collectors.toList());
            //防止缓存穿透
            if (collect == null || collect.isEmpty()) {
                //  缓存空结果，过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("小说列表为空"); // 或返回空对象，前端处理"章节不存在"
            }
            redisTemplate.opsForValue().set(key, collect, 10, TimeUnit.MINUTES);
            return collect;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<Map<String, Object>> getBooksDataList(String name) {
        String key = Const.SUB_BOOKS_DATA_LIST + ":" + name;
        if(redisTemplate.hasKey(key)){
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                // 缓存存在但值为null（已处理过穿透），直接返回空列表
                return Collections.emptyList();
            }
            return (List<Map<String, Object>>) value;
        }
        Lock lock = getLock(name+"1"); // 用name作为锁的key
        try {
            lock.lock();
            if(redisTemplate.hasKey(key)){
                Object value = redisTemplate.opsForValue().get(key);
                if (value == null) {
                    // 缓存存在但值为null（已处理过穿透），直接返回空列表
                    return Collections.emptyList();
                }
                return (List<Map<String, Object>>) value;
            }
            QueryWrapper<Books> objectQueryWrapper = new QueryWrapper<>();
            objectQueryWrapper.eq("category", name)
                    .select("novel_name", "author","latest_chapter","update_time","book_id")
                    .orderByDesc("update_time")
                    .last("LIMIT 20");
            List<Map<String, Object>> collect = booksMapper.selectMaps(objectQueryWrapper);
            //非空判断
            collect = collect != null ? collect : Collections.emptyList();
            // 1. 先提取所有bookId
            List<Integer> bookIds = collect.stream()
                    .map(map -> (Integer) map.get("book_id"))
                    .collect(Collectors.toList());
            // 2. 批量查询最新章节 // 1. 调用mapper获取列表结果
            String chapterTableName = chapterTablesMapper.getChapterTableName(name);
            List<Map<String, Object>> chapterList = chapterTablesMapper.batchGetChapterLastByBookIds(chapterTableName, bookIds);
            // 3. 转换为Map<Integer, Integer> (bookId -> maxChapterOrder)
            Map<Integer, Integer> bookIdToChapterOrder = chapterList.stream()
                    .collect(Collectors.toMap(
                            // 提取book_id作为key
                            map -> (Integer) map.get("book_id"),
                            // 提取max_chapter_order作为value
                            map -> {
                                Object orderObj = map.get("max_chapter_order");
                                // 处理可能的null值，默认为0
                                return orderObj != null ? (Integer) orderObj : 0;
                            },
                            // 处理重复key（如果存在），保留第一个值
                            (existing, replacement) -> existing
                    ));
            collect=collect.stream().map(map -> {
                map.put("title", map.get("novel_name"));
                map.remove("novel_name");
                map.put("chapter", map.get("latest_chapter"));
                map.remove("latest_chapter");
                map.put("updateTime", map.get("update_time").toString().substring(6));
                map.remove("update_time");
                map.put("category", name);
                Integer bookId = (Integer)map.get("book_id");
                map.put("bookId", bookId);
                map.remove("book_id");
                Integer chapterOrder = bookIdToChapterOrder.getOrDefault(bookId, 1);
                map.put("chapterOrder", chapterOrder);
                return map;
            }).collect(Collectors.toList());
            //防止缓存穿透
            if (collect == null || collect.isEmpty()) {
                //  缓存空结果，过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("小说列表为空"); // 或返回空对象，前端处理"章节不存在"
            }
            redisTemplate.opsForValue().set(key, collect, 10, TimeUnit.MINUTES);
            return collect;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取小说作者nookid and book
     * @param name
     * @return
     */
    @Override
    public List<Map<String, Object>> getBooksData(String name) {
        String key = Const.SUB_BOOKS_DATA + ":" + name;
        if(redisTemplate.hasKey(key)){
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                // 缓存存在但值为null（已处理过穿透），直接返回空列表
                return Collections.emptyList();
            }
            return (List<Map<String, Object>>) value;
        }
        Lock lock = getLock(name+"2"); // 用name作为锁的key
        try {
            lock.lock();
            if(redisTemplate.hasKey(key)){
                Object value = redisTemplate.opsForValue().get(key);
                if (value == null) {
                    // 缓存存在但值为null（已处理过穿透），直接返回空列表
                    return Collections.emptyList();
                }
                return (List<Map<String, Object>>) value;
            }
            QueryWrapper<Books> objectQueryWrapper = new QueryWrapper<>();
            objectQueryWrapper.eq("category", name)
                    .select("novel_name", "author","book_id")
                    .orderByDesc("recommend_count")
                    .last("LIMIT 20");
            List<Map<String, Object>> collect = booksMapper.selectMaps(objectQueryWrapper);
            //非空判断
            collect = collect != null ? collect : Collections.emptyList();
            collect=collect.stream().map(map -> {
                map.put("title", map.get("novel_name"));
                map.remove("novel_name");
                map.put("bookId", map.get("book_id"));

                map.remove("book_id");

                return map;
            }).collect(Collectors.toList());
            //防止缓存穿透
            if (collect == null || collect.isEmpty()) {
                //  缓存空结果，过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("小说列表为空"); // 或返回空对象，前端处理"章节不存在"
            }
            redisTemplate.opsForValue().set(key, collect, 10, TimeUnit.MINUTES);
            return collect;
        } finally {
            lock.unlock();
        }

    }

    /**
     * 搜索
     * @param keyword
     * @return
     */
    @Override
    public List<Map<String, Object>> searchBookByKayword(String keyword) {
        String key = Const.SEARCH_KEYWORD + ":" + keyword;
        if (redisTemplate.hasKey(key)) {
            Object value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                return Collections.emptyList();
            } else {
                // 缓存命中直接返回，可顺便续期
                redisTemplate.expire(key, 1, TimeUnit.MINUTES);
                return (List<Map<String, Object>>) value;
            }
        }
        Lock lock = getLock("search"+keyword); // 用name作为锁的key
        try {
            lock.lock();
            if (redisTemplate.hasKey(key)) {
                Object value = redisTemplate.opsForValue().get(key);
                return value == null ? Collections.emptyList() : (List<Map<String, Object>>) value;
            }
            QueryWrapper<Books> objectQueryWrapper = new QueryWrapper<>();
            objectQueryWrapper.like("novel_name", keyword)
                    .or()
                    .like("author", keyword)
                    .select("book_id","novel_name","category","intro", "author","update_time","cover_url")
                    .orderByDesc("recommend_count");
            List<Map<String, Object>> searchResults = booksMapper.selectMaps(objectQueryWrapper);
            searchResults  = searchResults.stream().map(map -> {
                map.put("bookId", map.get("book_id"));
                map.remove("book_id");
                map.put("name", map.get("novel_name"));
                map.remove("novel_name");
                map.put("description", map.get("intro"));
                map.remove("intro");
                map.put("updateTime", map.get("update_time").toString());
                map.remove("update_time");
                map.put("covel", map.get("cover_url"));
                map.remove("cover_url");
                return map;
            }).collect(Collectors.toList());
            if (searchResults == null || searchResults.isEmpty()) {
                //  缓存空结果，过期时间设短一点（1分钟）
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES);
                throw new RuntimeException("小说列表为空"); // 或返回空对象，前端处理"章节不存在"
            }
            redisTemplate.opsForValue().set(key, searchResults, 1, TimeUnit.MINUTES);
            return searchResults;

        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取分类
     *
     * @param bookId
     * @return
     */
    @Override
    public String getCategories(Long bookId) {
        String key = Const.CATEGORIES + ":" + bookId;
        if(redisTemplate.hasKey(key)){
            Object value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                redisTemplate.expire(key, 10, TimeUnit.MINUTES); // 缓存续期
                return value.toString();
            } else {
                return null; // 缓存值为null，直接返回
            }
        }
        Lock lock = getLock(bookId+"1");
        try {
            lock.lock();
            if (redisTemplate.hasKey(key)) {
                Object value = redisTemplate.opsForValue().get(key);
                return value != null ? value.toString() : null;
            }
                QueryWrapper<Books> QueryWrapper = new QueryWrapper<>();
                QueryWrapper.select("category")
                        .eq("book_id", bookId);

                String category = booksMapper.selectMaps(QueryWrapper).get(0).get("category").toString();
            if (category == null || category.isEmpty()) {
                redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES); // 缓存空结果防穿透
                return null;
            }
                redisTemplate.opsForValue().set(key, category, 10, TimeUnit.MINUTES);
                return category;
        }finally {
            lock.unlock();
        }
    }

    /**
     * 发送预缓存消息到MQ
     */
    private void sendPreCacheMessage(Integer bookId, Integer chapterId, String tableName) {
        try {
            ChapterPreCacheMessage message = new ChapterPreCacheMessage();
            message.setBookId(bookId);
            message.setChapterId(chapterId);
            message.setTableName(tableName);
            // 发送消息到章节预缓存队列
            rabbitTemplate.convertAndSend("chapter_pre_cache", message);
        } catch (Exception e) {
            // 记录日志，不影响主流程
            e.printStackTrace();
        }
    }

    /**
     * 获取书id对应的表名
     * @param bookId
     * @return
     */
    @NotNull
    public String getTableName(Integer bookId) {
        if(bookId <=0)
            throw new RuntimeException("参数错误");
//        获取分类类别
        String category =booksMapper.getCategoryById(bookId);
        if(category==null)
            throw new RuntimeException("系统错误");
//        获取章节表名
        String tableName =chapterTablesMapper.getChapterTableName(category);
        if(tableName==null)
            throw new RuntimeException("系统错误");
        return tableName;
    }

    /**
     * 生成子榜单数据（对应前端 sub 字段）
     * @param type：1=全站，2=男生，3=女生，4=新书，5=完本
     */
    private List<Map<String, Object>> getNameList(int type) {
        List<Map<String, Object>> subList = new ArrayList<>();

        // 根据分类类型，定义子榜单标题
        List<String> titles;
        switch (type) {
            case 1: // 全站
                titles = Arrays.asList("全站订阅周榜", "新书订阅周榜", "推荐周榜", "礼物周榜");
                break;
            case 2: // 男生
                titles = Arrays.asList("男生订阅周榜", "新书订阅周榜", "推荐周榜", "礼物周榜"
                );
                break;
            case 3: // 女生
                titles = Arrays.asList("女生订阅周榜", "新书订阅周榜", "推荐周榜", "礼物周榜"
                );
                break;
            case 4: // 新书
                titles = Arrays.asList("新书订阅周榜", "礼物周榜"
                );
                break;
            case 5: // 完本
                titles = Arrays.asList("完本订阅周榜", "完本点击周榜"
                );
                break;
            default:
                titles = new ArrayList<>();
        }
        // 为每个子榜单生成 novelName（查询数据库小说名）
        for (String title : titles) {
            // 查询对应榜单的小说名（根据 title 条件查询，这里以“订阅周榜”为例）
            QueryWrapper<Books> qw = new QueryWrapper<>();
            if (title.contains("订阅")) {
                qw.orderByDesc("subscribe_count"); // 按订阅量排序
            } else if (title.contains("推荐")) {
                qw.orderByDesc("recommend_count"); // 按推荐量排序
            } else if (title.contains("礼物")) {
                qw.orderByDesc("gift_count"); // 按礼物数排序
            } else if (title.contains("点击")) {
                qw.orderByDesc("click_count"); // 按点击量排序
            }
            // 叠加分类条件（如男生、女生）
            if (type == 2) {
                qw.eq("target_user", 1); // 男生
            } else if (type == 3) {
                qw.eq("target_user", 2); // 女生
            } else if (type == 4) {
                qw.gt("create_time", LocalDateTime.now().minusDays(7)); // 新书（30天内）
            } else if (type == 5) {
                qw.eq("status", "完结"); // 完本
            }
            qw.select("novel_name","book_id").last("limit 15"); // 最多15本

            List<Map<String, Object>> novelMaps = booksMapper.selectMaps(qw);
            Map<String, Object> subItem = new HashMap<>();
            subItem.put("title", title); // 子榜单标题
            subItem.put("novelName", new ArrayList<String>()); // 小说名列表
            subItem.put("bookId", new ArrayList<String>());    // 书籍ID列表
            for (int i = 0; i < novelMaps.size(); i++) {

                // 修正后
                Object bookIdObj = novelMaps.get(i).get("book_id");
                String bookId = bookIdObj != null ? String.valueOf(bookIdObj) : ""; // 处理null
                ((List<String>)subItem.get("bookId")).add(bookId);

                // 同理，novel_name如果是null也需要处理
                Object novelNameObj = novelMaps.get(i).get("novel_name");
                String novelName = novelNameObj != null ? (String) novelNameObj : "未知书名";
                ((List<String>)subItem.get("novelName")).add(novelName);
            }
            subList.add(subItem);
        }
        return subList;
    }
    /**
     * 获取小说名字和简介
     *
     * @return
     */
    public List<Map<String, Object>> getCoverUrlAndIntro(QueryWrapper<Books> queryWrapper) {
        queryWrapper
                .orderByDesc("recommend_count")
                .select("cover_url", "novel_name", "intro", "book_id")
                .last("limit 5");
        List<Map<String, Object>> nameAndIntro = booksMapper.selectMaps(queryWrapper);
        for (int i = 0; i < nameAndIntro.size(); i++) {
            //            改书名
            Object novel_name =  nameAndIntro.get(i).get("novel_name");
            nameAndIntro.get(i).remove("novel_name");
            nameAndIntro.get( i).put( "novelName", novel_name);
//            改头像名字
            Object cover_url =  nameAndIntro.get(i).get("cover_url");
            nameAndIntro.get(i).remove("cover_url");
            nameAndIntro.get(i).put( "coverUrl", cover_url);
//            改书id
            Object book_id =  nameAndIntro.get(i).get("book_id");
            nameAndIntro.get(i).remove("book_id");
            nameAndIntro.get(i).put( "bookId", book_id);
        }
        return nameAndIntro;
    }
    /**
     * 获取频道书籍列表
     * @param category
     */
    private List<Map<String, Object>> SubcategoryChapters(String category, int targetUser) {
        QueryWrapper<Books> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("target_user", targetUser)
                .eq("category", category)
                .orderByDesc("click_count")
                .select("novel_name", "category", "book_id", "cover_url", "intro","click_count");
        List<Map<String, Object>> Sub = booksMapper.selectMaps(queryWrapper1);
        return Sub;
    }

    /**
     * 抽取方法
     * @param boyAll
     * @return
     */
    private Map<String, Object> method1(List<Map<String, Object>> boyAll) {
        // 创建"全站"分类项
        Map<String, Object> allStation = new HashMap<>();
        allStation.put("title", "全站");
        allStation.put("category", new ArrayList<>());
        allStation.put("novelName", new ArrayList<>());
        allStation.put("coverUrl", new ArrayList<>());
        allStation.put("intro", new ArrayList<>());
        allStation.put("clickCount", new ArrayList<>());
        allStation.put("bookId", new ArrayList<>());
        // 最多添加10本书
        int count = Math.min(10, boyAll.size());
        for (int i = 0; i < count; i++) {
            Map<String, Object> book = boyAll.get(i);
            // 添加分类
            ((List<String>) allStation.get("category")).add((String) book.get("category"));
            // 添加小说名称
            ((List<String>) allStation.get("novelName")).add((String) book.get("novel_name"));
            // 添加封面URL
            ((List<String>) allStation.get("coverUrl")).add((String) book.get("cover_url"));
            // 添加简介
            ((List<String>) allStation.get("intro")).add((String) book.get("intro"));
            // 添加点击次数
            ((List<Integer>) allStation.get("clickCount")).add((Integer) book.get("click_count"));
            // 添加书id
            ((List<Integer>) allStation.get("bookId")).add((Integer) book.get("book_id"));
        }
        return allStation;
    }
    /**
     * 抽取方法
     * @param boyAll
     * @return
     */
    private Map<String, Object> method2(List<Map<String, Object>> boyAll, String category) {
        // 创建"全站"分类项
        Map<String, Object> allStation = new HashMap<>();
        allStation.put("title", category);
        allStation.put("category", new ArrayList<>());
        allStation.put("novelName", new ArrayList<>());
        allStation.put("coverUrl", new ArrayList<>());
        allStation.put("intro", new ArrayList<>());
        allStation.put("clickCount", new ArrayList<>());
        // 最多添加10本书
        int count = Math.min(10, boyAll.size());
        for (int i = 0; i < count; i++) {
            Map<String, Object> book = boyAll.get(i);
            // 添加分类
            ((List<String>) allStation.get("category")).add((String) book.get("category"));
            // 添加小说名称
            ((List<String>) allStation.get("novelName")).add((String) book.get("novel_name"));
            // 添加封面URL
            ((List<String>) allStation.get("coverUrl")).add((String) book.get("cover_url"));
            // 添加简介
            ((List<String>) allStation.get("intro")).add((String) book.get("intro"));
            // 添加点击次数
            ((List<Integer>) allStation.get("clickCount")).add((Integer) book.get("click_count"));
        }
        return allStation;
    }




}
